﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Collections;
using System.Text.RegularExpressions;
using MFWebSocket.Contract;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace MFWebSocket.Contract.Clients
{
    /// <summary>
    /// A Micro Framework WebSocket Protocol implementation contributed by Julius Friedman. 
    /// Based on the code from the <see cref="http://superwebsocket.codeplex.com/">SuperWebSocket</see> CodePlex project
    public class WebSocketClient : IWebSocketClient, IDisposable
    {

        #region [Constants]

        /// <summary>
        /// The default amount of time we will wait when sending or recieving in milliseconds.
        /// </summary>
        const int DefaultSocketSendRecieveTimeoutMS = 10000,//10 seconds
        
        /// <summary>
        /// The default amount of bytes * 2 WebSocketClients will use as a buffer in memory
        /// </summary>
        DefaultSocketBufferSize = 256,//bytes * 2

        /// <summary>
        /// In general the maximum size of a IP Tcp PDU
        /// </summary>
        MaxTcpLength = 2048;

        /// <summary>
        /// A string representing the WebSocket Protocol Scheme
        /// </summary>
        const string wsURI = "ws",

        /// <summary>
        /// A string representing th WebSocket Secure Protocol Scheme
        /// </summary>
        wssURI = "wss";

        /// <summary>
        /// All messages in a WebSocket stream start with this mark.
        /// </summary>
        internal const byte m_DataFrameStart = 0x00,
        
        /// <summary>
        /// All messages in a WebSocket stream end with this mark.
        /// </summary>
        m_DataFrameEnd = 0xFF;

        /// <summary>
        /// The New Line Mark.
        /// </summary>
        static readonly byte[] m_NewLineMark = {0x0d, 0x0a, 0x0d, 0x0a};

        #endregion

        #region Fields

        #region [Protected]

        ///// <summary>
        ///// The default amount of bytes this WebSocketClient uses to buffer for sending messages
        ///// </summary>
        //protected int m_SendBufferSize = DefaultSocketBufferSize,//bytes
        
        /// <summary>
        /// The default amount of bytes this WebSocketClient uses to buffer for recieving sending messages
        /// </summary>    
        protected int m_SocketBufferSize = DefaultSocketBufferSize * 2;//bytes

        /// <summary>
        /// Instance string fields. <see cref="region#Properties"/> for more information
        /// </summary>
        protected string m_Protocol = string.Empty, m_Host = string.Empty, m_Path = string.Empty;
        
        /// <summary>
        /// Instance field for the host's Uri
        /// </summary>
        protected Uri m_Uri;
        
        /// <summary>
        /// Boolean instance fields to indicate state
        /// </summary>
        protected bool m_Connected, m_InSending, m_Disposed, m_Handshaked;
        
        /// <summary>
        /// The soket this isntance uses to communicate
        /// </summary>
        protected Socket m_Socket;
        
        /// <summary>
        /// The collection of cookies utilized by this WebSocketClient instance
        /// </summary>
        protected HttpCookieCollection m_Cookies;
        
        /// <summary>
        /// The handshake response this WebSocketClient recieved upon connection to the host.
        /// Usually 16 bytes.
        /// </summary>
        protected byte[] m_HandShakeBytes,

        /// <summary>
        /// The Socket buffer. A Single contagious allocation of memory for higher performance with Sockets and the CLR.
        /// Always 2 * RecieveBufferSize
        /// (With  Default RecieveBufferSize = 128 Bytes 256 Send{0, 128} and Recieve {127, 255})
        /// </summary>
        m_SocketBuffer;

        #endregion

        #region [Private]
        
        /// <summary>
        /// THe RemoteEndPoint
        /// </summary>
        EndPoint remoteEndPoint;
        
        /// <summary>
        /// The amount of bytes send and recived
        /// </summary>
        /*volatile*/
        int m_BytesSent, m_BytesRecieved;
        
        /// <summary>
        /// The bufferd messages to be send
        /// </summary>
        ArrayList m_MessBuilder = new ArrayList();//<byte>
        
        /// <summary>
        /// The worker thread this WebSocketClient instance uses
        /// </summary>
        Thread m_Worker;

        /// <summary>
        /// The message queue this WebSocketClient instance uses to buffer messages to be sent to the host
        /// </summary>
        ConcurrentQueue m_MessagesBeingSent = new ConcurrentQueue();//<MessageArgs>

        #endregion

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of a WebSocketClient with no defined protocol.
        /// </summary>
        /// <param name="uri">The host's Uri</param>
        public WebSocketClient(string uri) : this(uri, null) { }

        /// <summary>
        /// Creates a new instance of a WebSocketClient.
        /// </summary>
        /// <param name="uri">The host's Uri</param>
        /// <param name="protocol">The WebSocketProtocol if applicable</param>
        public WebSocketClient(string uri, string protocol = null) : this(new Uri(uri), null, protocol) { }

        /// <summary>
        /// Creates a new instance of a WebSocketClient.
        /// </summary>
        /// <param name="uri">The host's Uri</param>
        /// <param name="cookies">Any HttpCookies to be sent along with the connection</param>
        /// <param name="protocol">The WebSocketProtocol if applicable</param>
        ///// <param name="sendBufferSize">The size of the send buffer</param>
        /// <param name="receiveBufferSize">The size of the recieve buffer</param>
        public WebSocketClient(Uri uri, HttpCookieCollection cookies, string protocol = null, /*int sendBufferSize = DefaultSocketBufferSize,*/ int receiveBufferSize = DefaultSocketBufferSize)
        {
            ///Sanity checks
            if (uri == null) throw new ArgumentNullException("uri");

            ///Determine if the scheme is correct and supported
            if (!(uri.Scheme == wsURI))
            {
                if (uri.Scheme == wssURI)
                {
                    throw new ArgumentException("wss is not supported yet!");
                }
                else
                {
                    throw new ArgumentException("Invalid websocket address!");
                }
            }

            //Assign the Uri
            m_Uri = uri;

            //Assign the Protocol if empty it will not be included
            m_Protocol = protocol;

            //Validate uri and get index of Path in 1 shot
            int pos = uri.AbsoluteUri.IndexOf('/', 5);
            if (pos <= 0) throw new ArgumentException("Invalid websocket address!");
            //Assign path
            else m_Path = uri.AbsoluteUri.Substring(pos);

            //Assign the port and host
            //Determine host from path
            //string host = uri.AbsoluteUri.Substring(5, pos - 5);

            //Split the host and port
            string[] hostInfo = uri.AbsoluteUri.Substring(5, pos - 5).Split(':');

            //validate
            if (hostInfo.Length != 2) throw new ArgumentException("Invalid websocket address! Ensure the port number is provided :80 for example.");
            else
            {

                int port;
                //Assign the port
                try { port = int.Parse(hostInfo[1]); }
                catch { throw new ArgumentException("Invalid websocket address!"); }
                //Assign the host
                m_Host = hostInfo[0];
                //Get the WebSocketClient Host and Port Information
                IPAddress ipAddress = null;
                try
                {
                    //Is it a IPAddress?
                    ipAddress = IPAddress.Parse(m_Host);
                    remoteEndPoint = new IPEndPoint(ipAddress, port);
                }
                catch (ArgumentException)//Try a DNS Entry...
                {
                    try { remoteEndPoint = new IPEndPoint(Dns.GetHostEntry(m_Host).AddressList[0], port); }
                    catch { throw; }//We don't know who we are this shouldn't happen. The host we determined was neither a IPAddress or a resolveable DNS host                
                }
                catch (ThreadAbortException) { return; }
                catch { throw; }
                finally
                {
                    //Save Cookies in member
                    m_Cookies = cookies;

                    //Attach Handlers
                    Open += OpenHandler;
                    Message += MessageHandler;
                    Close += CloseHandler;

                    //Allocate buffer memory required coherently
                    //Send not buffered
                    //if (sendBufferSize > 0 && sendBufferSize <= MaxTcpLength) m_SendBufferSize = sendBufferSize;                        
                    if (receiveBufferSize > 0 && receiveBufferSize <= MaxTcpLength) m_SocketBufferSize = receiveBufferSize;
                    m_SocketBuffer = new byte[/*m_SendBufferSize +*/ m_SocketBufferSize];
                }
            }
        }

        #endregion

        #region Destructor

        /// <summary>
        /// Disposes this WebSocketClient and removes all references allocated in the instance
        /// </summary>
        ~WebSocketClient()
        {
            //Dispose the WebSocketClient
            Dispose();
            //Remove references to members
            m_Cookies = null;
            m_HandShakeBytes = m_SocketBuffer = null;
            m_MessagesBeingSent = null;
            m_MessBuilder = null;
        }

        #endregion

        #region Methods

        #region [Public]

        /// <summary>
        /// Connect the WebSocketClient to the Host.
        /// Spawns a worker thread so this function can return immediately.        
        /// </summary>
        public void Connect()
        {
            //If we are connected disposed or already processing
            if (m_Connected || m_Disposed || m_Worker != null) return;          
  
            // Start a new thread so we can immediately return.
            m_Worker = new Thread(() =>
            {
                try
                {
                    //Connect the socket
                    ProcessConnect(DefaultSocketSendRecieveTimeoutMS, DefaultSocketSendRecieveTimeoutMS);
                }
                catch (ThreadAbortException) { return; }
                catch { throw; }//Throw exceptions
            });

            //Set the priority of the worker
            m_Worker.Priority = ThreadPriority.AboveNormal;

            //Start the worker
            m_Worker.Start();
        }

        /// <summary>
        /// Handles the Message event.
        /// Called with Message Recieved by the WebSocektClient when it arrives on the socket, can be overriden in a derived class
        /// </summary>
        /// <param name="sender">The WebSocketClient in which the message was recieved on</param>
        /// <param name="args">The MessageArgs which contian the message recieved by the WebSocketClient</param>
        public virtual void MessageHandler(IWebSocketClient sender, MessageArgs args) { }

        /// <summary>
        /// Disposes this WebSocketClient
        /// (Throws ObjectDisposedException if already disposed.)
        /// </summary>
        public virtual void Dispose()
        {
            //If we are disposed we must throw
            if (m_Disposed == true) throw new ObjectDisposedException();
            //Dipose the WebSocket Client
            try
            {
                //Indicate disposed
                m_Disposed = true;
                //Close the socket
                CloseSocket();
                StopWorker();
            }           
            catch (SocketException)
            {
                ;//Do nothing
            }
            catch
            {
                //OutOfMemory or otherwise
                throw;
            }
            finally
            {
                //Unscope the worker
                m_Worker = null;
                //Remove Events                
                this.Open -= this.OpenHandler;
                this.Message -= MessageHandler;
                this.Close -= CloseHandler;
            }
        }

        #endregion

        #region [Private]

        /// <summary>
        /// Used by the worker.
        /// Starts recieving bytes from the connected socket
        /// </summary>
        void StartReceive()
        {
            try
            {
                ///Recieve in chunks from a common buffer
                ///Count the bytes recieved
                int recieved = m_Socket.ReceiveFrom(m_SocketBuffer, /*m_SendBufferSize*/0, m_SocketBufferSize, SocketFlags.None, ref remoteEndPoint);
                //Increment the recieved counter
                this.m_BytesRecieved += recieved;
                //Process the recieved data if we have any and we are connected
                if (recieved > 0 && m_Connected) ProcessReceive(m_SocketBuffer, RecieveBufferSize, recieved);
                //If we are not already in a sending routine divert to sending until all sent messages have been processed
                if (!m_InSending && m_Connected) StartSendFromQueue();                
            }
            catch(SocketException sEx)
            {
                //Handle SocketException
                SocketException(sEx);
            }
            catch
            {
                //This is something we cannot predict in advance
                throw;
            }                        
        }

        /// <summary>
        /// Used by the worker.
        /// Processes messages waiting to be sent from the message queue.
        /// </summary>
        void StartSendFromQueue()
        {
            //A boxed string
            object oMsg = null;
            //If we can't get a message return
            if (!(m_InSending = m_MessagesBeingSent.TryDequeue(out oMsg))) return;
            else do
                {
                    //Get the dequeued message out of the object box
                    MessageArgs message = oMsg as MessageArgs;
                    //Send it
                    try
                    {
                        ProcessSend(ref message);
                    }
                    catch
                    {
                        //Whatever happened all it means to us (Worker) is that we should stop sending
                        break;
                    }
                    finally
                    {
                        oMsg = null;
                    }
                    //While there is still a object in the message to unbox                        
                } while (m_InSending = m_MessagesBeingSent.TryDequeue(out oMsg));
        }

        /// <summary>
        /// Used by the worker.
        /// Sends a message on the WebSocket encoding the Message in a DataFrame according to the WebSocketProtocol
        /// </summary>
        /// <param name="message">The message being sent</param>
        void ProcessSend(ref MessageArgs messageArgs)
        {
            if (null == messageArgs || null == messageArgs.DataFrame) return;
            try
            {
                //Incrment the sent counter
                m_BytesSent += m_Socket.SendTo(messageArgs.DataFrame, 0, messageArgs.DataFrame.Length, SocketFlags.None, remoteEndPoint);
#if DEBUG
                //Print the bytes sent
                Debug.Print("Sent bytes: " + messageArgs.Message);
#endif
            }
            catch (SocketException sEx)
            {
                //Handle SocketException
                SocketException(sEx);
            }
            catch (ThreadAbortException) { return; }
            catch { throw; }        
        }

        /// <summary>
        /// Used by the worker.
        /// Processes the recieved data into DataFrames based of the WebSocketProtocol
        /// </summary>
        /// <param name="buffer">The buffer to process</param>
        /// <param name="offset">The start index</param>
        /// <param name="length">The length</param>
        void ProcessReceive(byte[] buffer, int offset, int length)
        {
            //First data or Partial Data Frame
            if (m_MessBuilder.Count <= 0)
            {
                //Determine where to start
                int startPos = Array.IndexOf(buffer, m_DataFrameStart, 0);

                //We we should not start yet then return
                if (startPos < 0) return;

                //Determine where to end
                int endPos = Array.IndexOf(buffer, m_DataFrameEnd, startPos, offset + length - startPos);//buffer.IndexOf(m_EndByte, startPos, offset + length - startPos);

                //If we are not at the end yet rebuffer
                if (endPos < 0)
                {
                    //add the bytes which are part of the message to the message builder
                    foreach (byte b in Microsoft.SPOT.Hardware.Utility.ExtractRangeFromArray(buffer, offset, length)) m_MessBuilder.Add(b);
                    //dispatch an event if we are connected
                    if (m_Connected) Message(this, new MessageArgs(new String(Encoding.UTF8.GetChars(buffer), startPos + 1, endPos - startPos - 1)));
                }
                //If we do not have more data return
                if (endPos >= (offset + length - 1)) return;
                //Process more data
                else ProcessReceive(buffer, endPos + 1, offset + length - endPos - 1);

            }
            else//Subsequent Data from Common Buffer
            {
                //Determine end position                
                int endPos = Array.IndexOf(buffer, m_DataFrameEnd, offset, length);
                //If we are not at the end yet
                if (endPos < 0)
                {
                    //Rebufer
                    foreach (byte b in Microsoft.SPOT.Hardware.Utility.ExtractRangeFromArray(buffer, offset, length)) m_MessBuilder.Add(b);
                    //Return
                    return;
                }

                //Dispatch the event
                //Skip the mark at the first byte
                int len = endPos - offset;
                Message(this, new MessageArgs(new String(Encoding.UTF8.GetChars(Microsoft.SPOT.Hardware.Utility.ExtractRangeFromArray(buffer, offset, len)), offset, len)));

                //If we are past the end return
                if (endPos >= (offset + length - 1)) return;

                //Process more data
                ProcessReceive(buffer, endPos + 1, offset + length - endPos - 1);
            }
        }

        #endregion

        #region [Protected]

        /// <summary>
        /// Starts the worker
        /// </summary>
        protected virtual void StartWorker()
        {
            if (m_Worker != null) return;
            try
            {
                //Connect the socket
                ProcessConnect(DefaultSocketSendRecieveTimeoutMS, DefaultSocketSendRecieveTimeoutMS);
            }
            catch
            {
                //Throw exceptions
                throw;
            }
        }

        /// <summary>
        /// Stops the worker
        /// </summary>
        protected virtual void StopWorker()
        {
            try
            {
                //Joing or Abort the Worker Thread
                if (!m_Worker.Join(m_Socket.ReceiveTimeout)) m_Worker.Abort();
            }
            catch (ThreadAbortException)
            {
                return;
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Close the socket spawn a Close event
        /// </summary>
        protected void EnsureCloseSocket()
        {
            //If the m_Socket is here we don't need it anymore
            if (m_Socket != null)
            {
                try
                {
                    //Close the socket
                    m_Socket.Close();
                }
#if DEBUG
                catch (Exception ex)
                {
                    Tools.LogException("WebSocketClient:EnsureCloseSocket=>", ex);
#else
                catch
                {
                    ;//Do nothing    
#endif
                }
                finally
                {
                    //Set the socket to nil
                    m_Socket = null;
                    //If we were connected 
                    if (m_Connected)
                    {
                        //We are no longer connected
                        m_Connected = false;
                        //dispatch an event                        
                        Close(this);
                    }                    
                }
            }
        }

        /// <summary>
        /// Handles open events. Starts the recieve process. Can be overriden in a derived class
        /// </summary>
        /// <param name="sender">The IWebSocketClient which just connected.</param>
        protected virtual void OpenHandler(IWebSocketClient sender) { }

        /// <summary>
        /// Handles the close events. Ensures the socket is closed.
        /// </summary>
        /// <param name="sender">The IWebSocketClient which just connected.</param>
        protected void CloseHandler(IWebSocketClient sender)
        {
            EnsureCloseSocket();
        }


        /// <summary>
        /// Handles the handshake portion of the connection process.        
        /// </summary>        
        protected virtual void ProcessHandshake()
        {
            //The first response usually looks something like this and is in response to the Handshake
            /*
             * HTTP/1.1 101 WebSocket Protocol Handshake\r\n
             * Upgrade: WebSocket\r\nConnection: Upgrade\r\n
             * Sec-WebSocket-Location: {m_Uri.AbsoluteUri}\r\n
             * Sec-WebSocket-Origin: {Host}\r\n\r\n
             * {HandShakeBytes}{DataFrame}
                 
             * * Where Data Frame = {0x00}{DATA}{0x255}
                 
             * * For Pusher {DATA} = something like below
             * An event - The name of the event,             some data - JSON Encoded
             * {\"event\":\"pusher:connection_established\",\"data\":\"{\\\"socket_id\\\":\\\"1188.541563\\\"}\"}
             * Only the {DATA} portion is given to the event. The expected response is in the m_HandShakeResponse field
            */

            //If we didn't recieve any bytes or there are none available then we shouldn't try to process any
            if (m_BytesRecieved > 0 || m_Socket.Available > 0)
            {
            MatchHandShakeBytes:
                //Determine if the bytes match by looking for for the m_NewLineMark to determine the start of the HandShakeBytes
                //int start = Tools.IndexOfBytes(m_HandShakeBytes, index, m_RecieveBufferSize, m_HandShakeBytes.Length, m_HandShakeBytes);
                int start = Tools.IndexOfBytes(m_NewLineMark, m_SocketBufferSize - 1, m_NewLineMark.Length, m_HandShakeBytes, -2);
                //If the handshake does not match throw an exception
                if (!Tools.Matches(m_SocketBuffer, start, m_HandShakeBytes.Length, m_HandShakeBytes) && m_Socket.Available == 0) throw new WebSocketClientException("Handshake doesn't match");
                //if (start == -1 && m_Socket.Available == 0) throw new WebSocketClientException("Handshake doesn't match");
                //Otherwise while we have recieved less then the maximum Tcp Size we will recieve again in chunks (this way someone cannot inject packets during this process keeping this section in a infinte loop)
                else while (m_Socket.Available > 0 && m_BytesRecieved < MaxTcpLength)
                {
                    StartReceive(); goto MatchHandShakeBytes;
                }                   
                
                //Indicate the handshake was successful
                m_Handshaked = true;
                
                //Handle any messages on the wire after the handshake
                ProcessReceive(m_SocketBuffer, m_SocketBufferSize, m_SocketBufferSize);
            }
            else
            {
                //Clear the HandShakeBytes
                m_HandShakeBytes = Tools.emptyBytes;
#if DEBUG
            Debug.Print("No handshake recieved");
#endif
            }
        }

        /// <summary>
        /// Build a Handshake which can be used to Connect to the Host. Puts the expected respose from the host in the given buffer
        /// </summary>
        /// <param name="expectedResponse">The buffer to place the response to the returned handshake.</param>
        /// <returns>A WekSocket Handshake String</returns>
        protected virtual String BuildHandShake(out byte[] expectedResponse)
        {
            //Generate Security Keys
            string secKey1 = new String(Encoding.UTF8.GetChars(WebSocketClient.GenerateSecKey()));

            string secKey2 = new String(Encoding.UTF8.GetChars(WebSocketClient.GenerateSecKey()));

            byte[] secKey3 = WebSocketClient.GenerateSecKey(8);

            //Generate Expected Response
            expectedResponse = WebSocketClient.GetResponseSecurityKey(secKey1, secKey2, secKey3);

            //Build Handshake
            StringBuilder handshakeBuilder = new StringBuilder();

            handshakeBuilder.AppendLine(string.Concat("GET ", Path, " HTTP/1.1"));
            handshakeBuilder.AppendLine("Upgrade: WebSocket");
            handshakeBuilder.AppendLine("Connection: Upgrade");
            handshakeBuilder.AppendLine(string.Concat("Sec-WebSocket-Key2: ", secKey2));
            handshakeBuilder.AppendLine(string.Concat("Host: ", Host));
            handshakeBuilder.AppendLine(string.Concat("Sec-WebSocket-Key1: ", secKey1));
            handshakeBuilder.AppendLine(string.Concat("Origin:", Host));

            //If the protocol was specified then include it
            if (m_Protocol != null && m_Protocol != "") handshakeBuilder.AppendLine(string.Concat("WebSocket-Protocol: ", m_Protocol));

            //Include Cookies and Security Key
            if (m_Cookies != null && m_Cookies.Count > 0)
            {
                StringBuilder cookiePairs = new StringBuilder();
                for (int i = 0, e = m_Cookies.Count; i < e; ++i)
                {
                    HttpCookie item = m_Cookies[i];
                    cookiePairs.Append(item.Name);
                    cookiePairs.Append('=');
                    cookiePairs.Append(Tools.EscapeUriString(item.Value));
                    cookiePairs.Append(';');
                }
                handshakeBuilder.AppendLine(string.Concat("Cookie:", cookiePairs.ToString()));
            }
            handshakeBuilder.AppendLine();
            handshakeBuilder.Append(Encoding.UTF8.GetChars(secKey3));

            return handshakeBuilder.ToString();
        }

        /// <summary>
        /// Socket Exception Handler
        /// </summary>
        /// <param name="sEx">The Socket Exception which occured</param>
        protected void SocketException(SocketException sEx)
        {
            ///Switch on the ErrorCode
            switch (sEx.ErrorCode)
            {
                case 10050://Did not send the data within the defined timeout
                case 10060://Did not recieve a response within the defined timeout
                    return;
                default:
                    throw sEx;
            }                        
        }

        /// <summary>
        /// Used by the worker.
        /// Connect to the Host. Derived classes are encouraged to utilize the Message Event rather then override but the option is available.
        /// </summary>
        /// <param name="sendTimeout">The Send Timeout in Milliseconds</param>
        /// <param name="recieveTimeout">The Recive Timeout in Milliseconds</param>
        protected virtual void ProcessConnect(int sendTimeout = DefaultSocketSendRecieveTimeoutMS, int recieveTimeout = DefaultSocketSendRecieveTimeoutMS)
        {
            if (m_Disposed) throw new ObjectDisposedException();
            //Attempt to connect if we are not connected
            if (m_Connected) return;
            try
            {
                //Get the bytes for the wire from the handshake and send them (block scope of all byte[] expect expectedResponse)
                //Make the Socket
                m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                m_Socket.SendTimeout = sendTimeout;
                m_Socket.ReceiveTimeout = recieveTimeout;
                //Connect
                m_Socket.Connect(RemoteEndPoint);
#if DEBUG
                //Increment the send counter and send the bytes
                //Put the expected response in the m_HandShakeResponse field
                string sent = BuildHandShake(out m_HandShakeBytes);
                m_BytesSent += m_Socket.SendTo(Encoding.UTF8.GetBytes(sent), remoteEndPoint);
                //Debug Checking
                {
                    Debug.Print("Sent bytes: " + sent);
                }
#else
                //Build handshake and get expectedResponse and send the bytes
                //Incrment the send counter and send the bytes                
                m_BytesSent += m_Socket.SendTo(Encoding.UTF8.GetBytes(BuildHandShake(out m_HandShakeBytes)), SocketFlags.None, remoteEndPoint);
#endif

                //Wait for the response, recieve the response into the common buffer
                m_BytesRecieved = m_Socket.ReceiveFrom(m_SocketBuffer, 0, m_SocketBufferSize, SocketFlags.None, ref remoteEndPoint);
#if DEBUG
                //Print the bytes recieved if we have any
                //This should be the handshake response
                if (m_BytesRecieved > 0)
                    Debug.Print("Recieved bytes: " + Tools.GetString(m_SocketBuffer, RecieveBufferSize, m_BytesRecieved));
#endif

            }
            catch (System.Net.Sockets.SocketException sEx)
            {
                //Handle SocketException
                SocketException(sEx);
            }
            catch
            {
                //An error occured we must dispose this instance
                Dispose();
                //Bubble exception keeping the stack.
                throw;
            }


            ProcessHandshake();

            //Indicate connected
            m_Connected = true;

            //dispatch an Event
            Open(this);

            try
            {
                //Recieve until we have to stop
                while (true) StartReceive();
            }           
            catch
            {
                throw;//If the thread abortd we can just bubble it up, the aborter should catch the exception
            }
        }

        /// <summary>
        /// Closes the WebSocketClient
        /// </summary>
        public void CloseSocket()
        {
            CloseHandler(this);
        }

        /// <summary>
        /// Sends a message using the WebSocketClient
        /// </summary>
        /// <param name="message">The message to be sent</param>
        public void Send(string message)
        {
            if (m_Disposed)
                throw new ObjectDisposedException();

            if (!m_Connected)
                throw new Exception("The websocket is not open, so you can not send a message now!");

            //Queue the message
            m_MessagesBeingSent.Enqueue(new MessageArgs(message));

            //We we are not in sending then StartToSend
            if (!m_InSending) StartSendFromQueue();                
        }

        #endregion

        #region [Internal - Static]

        /// <summary>
        /// Generate a ResponseSecurity Key for the WebSocketProtocol
        /// </summary>
        /// <param name="secKey1">Key-1</param>
        /// <param name="secKey2">Key-2</param>
        /// <param name="secKey3">Key-3</param>
        /// <returns>The ResponseSecurityKey</returns>
        internal static byte[] GetResponseSecurityKey(string secKey1, string secKey2, byte[] secKey3)
        {
            //Remove all symbols that are not numbers
            Regex replacer = new Regex("[^0-9]");
            //StringBuilder k1 = new StringBuilder(secKey1).Replace(" ", "");
            //Tools.ReplaceExtraChars(ref k1);
            //StringBuilder k2 = new StringBuilder(secKey2).Replace(" ", "");
            //Tools.ReplaceExtraChars(ref k2);

            string k1 = replacer.Replace(secKey1, "");//Regex.Replace(secKey1, "[^0-9]");
            string k2 = replacer.Replace(secKey2, "");//Regex.Replace(secKey2, "[^0-9]");

            //Convert received string to 64 bit integer.
            Int64 intK1 = Int64.Parse(k1.ToString());
            Int64 intK2 = Int64.Parse(k2.ToString());

            //Dividing on number of spaces
            int k1Spaces = Tools.CountChars(ref secKey1, ref Tools.Space);//secKey1.Count(c => c == Space);
            int k2Spaces = Tools.CountChars(ref secKey2, ref Tools.Space);//secKey2.Count(c => c == Space);
            int k1FinalNum = (int)(intK1 / k1Spaces);
            int k2FinalNum = (int)(intK2 / k2Spaces);

            //Getting byte parts
            byte[] b1 = new byte[4];//BitConverter.GetBytes(k1FinalNum).Reverse().ToArray();
            Microsoft.SPOT.Hardware.Utility.InsertValueIntoArray(b1, 0, 4, (uint)k1FinalNum);
            Tools.Reverse(b1);

            byte[] b2 = new byte[4];//BitConverter.GetBytes(k2FinalNum).Reverse().ToArray();
            Microsoft.SPOT.Hardware.Utility.InsertValueIntoArray(b2, 0, 4, (uint)k2FinalNum);
            Tools.Reverse(b2);

            byte[] b3 = Encoding.UTF8.GetBytes(new String(Encoding.UTF8.GetChars(secKey3)));

            ////Concatenating everything into 1 byte array for hashing.
            //int l1 = b1.Length, l2 = b2.Length, l3 = b3.Length;
            //byte[] bChallenge = new byte[l1 + l2 + l3];
            //b1.CopyTo(bChallenge, 0);
            //b2.CopyTo(bChallenge, l1);
            //b3.CopyTo(bChallenge, l2);

            //Hash and return
            return MD5.GetHash(Tools.CombineArrays(b1, b2, b3));
        }

        /// <summary>
        /// Generates a random security key for use in the WebSocketClient connection process.
        /// </summary>
        /// <returns></returns>
        internal static byte[] GenerateSecKey()
        {
            //Generate a Random length between 10 and 20
            return GenerateSecKey(Tools.Random(10, 20));
        }

        /// <summary>
        /// Generates a random security key of the given total length
        /// </summary>
        /// <param name="totalLen">The total length of the security key to generate</param>
        /// <returns>The generated random security key</returns>
        internal static byte[] GenerateSecKey(int totalLen)
        {
            //totalLen Minus 1 =>(tM1)
            int tM1 = totalLen - 1;

            //Generate a Random length between 1 and totalLen /2 +1
            int spaceLen = Tools.Random(1, totalLen / 2 + 1);

            //Generate a Random length between 3 and totalLen -1 - spaceLen
            int charLen = Tools.Random(3, tM1 - spaceLen);

            //Determine the lengh of digits
            int digLen = totalLen - spaceLen - charLen;


            //The buffer
            //byte[] source = new byte[totalLen];
            ArrayList source = new ArrayList();//<char>

            //Padding
            for (int i = 0; i < spaceLen; ++i) source.Add(Tools.Space);

            //Get a Random character for the key from the generated chars
            for (int i = 0; i < charLen; ++i) source.Add((char)Tools.Random((int)'A', (int)'z'));

            for (int i = 0; i < digLen; ++i) source.Add((char)Tools.Random((int)'0', (int)'9'));

            ///<summary>
            /// //Cipher the key (using the max = (tM1 = totalLen - 1) => In cases of say 3 we have {x,y,z}, => 2 {0, 1} => 1 {0})
            ///</summary>

            //The ciphered bytes
            byte[] mixedChars = new byte[totalLen];
            //until i < tM1
            for (int i = 0; i < tM1; ++i)
            {
                //Get a position which is random and within the count
                int pos = Tools.Random(source.Count - 1);
                //Byte in a Box
                object toAddRemove = source[pos];
                byte b = Tools.__BYTE_UNBOX__(toAddRemove);
                //Set the byte in the cipher to byte at the buffer
                mixedChars[i] = b;
                //Remove the character so we cannot get it again in the cipher
                source.Remove(toAddRemove);
                //its now garbadge
                toAddRemove = null;
            }
            //Ensure the last byte from the source was the first
            mixedChars[tM1] = Tools.__BYTE_UNBOX__(source[0]);
            //Return the result
            return mixedChars;
        }

        //internal static int WebSocketClient_Socket_ProcessReceive(ref Socket socket, ref byte[] buffer)
        //{
        //    //TODO
        //    throw new NotImplementedException();
        //}
        //internal static int WebSocketClient_Socket_ProcessSend(ref Socket socket, ref string message)
        //{
        //    //TODO
        //    throw new NotImplementedException();
        //}

        #endregion

        #endregion

        #region Events

        /// <summary>
        /// The event fired when the WebSocketClient is connected and successfuly handshaked if applicable.
        /// </summary>
        public event OpenHandler Open;
        
        /// <summary>
        /// The event fired when the WebSocketClient is closed.
        /// </summary>
        public event CloseHandler Close;

        /// <summary>
        /// The event fired when the WebSocketClient recieves a message.
        /// </summary>
        public event MessageHandler Message;

        #endregion

        #region Properties

        /// <summary>
        /// Indicates if Dipose has been called of this WebSocketClient
        /// </summary>
        public virtual bool Disposed
        {
            get
            {
                return m_Disposed;
            }
        }

        /// <summary>
        /// The Uri Information of the WebSocketClient
        /// </summary>
        public Uri Uri
        {
            get
            {
                return m_Uri;
            }
        }

        /// <summary>
        /// The Path this WebSocketClient is recieving against
        /// </summary>
        public string Path
        {
            get
            {
                return m_Path;
            }
        }

        /// <summary>
        /// The Host of this WebSocketClient
        /// </summary>
        public string Host
        {
            get
            {
                return m_Host;
            }
        }

        /// <summary>
        /// A Count of the Bytes Sent by this WebSocketClient
        /// </summary>
        public int BytesSent
        {
            get
            {
                return m_BytesSent;
            }
        }

        ///// <summary>
        ///// The amount of bytes this WebSocketClient uses to send messages
        ///// </summary>
        //public int SendBufferSize
        //{
        //    get
        //    {
        //        return m_SendBufferSize;
        //    }
        //}

        /// <summary>
        /// A Amoutn of bytes this WebSocketClient uses to recieve messages
        /// </summary>
        public int RecieveBufferSize
        {
            get
            {
                return m_SocketBufferSize;
            }
        }

        /// <summary>
        /// A Count of the BytesRecieved by this WebSocketClient
        /// </summary>
        public int BytesRecieved
        {
            get
            {
                return m_BytesRecieved;
            }
        }

        /// <summary>
        /// Indicates if this WebSocketClient is in the progress of sending
        /// </summary>
        public bool Sending
        {
            get
            {
                return m_InSending;
            }
        }

        /// <summary>
        /// Indicates if this WebSocketClient has been successfully connected to the host
        /// </summary>
        public bool Connected
        {
            get
            {
                return m_Connected;
            }
        }

        /// <summary>
        /// Indicates if this WebSocketClient has handshaked with the host in the current connection
        /// </summary>
        public bool Handshaked
        {
            get { return m_Handshaked; }
        }

        /// <summary>
        /// The Bytes of the Handshake this WebSocketClient recieved from the host
        /// </summary>
        public byte[] HandshakeBytes
        {
            get { return m_HandShakeBytes; }
        }

        /// <summary>
        /// The LocalEndPoint of this WebSocketClient
        /// </summary>
        public IPEndPoint LocalEndPoint
        {
            get
            {
                return (IPEndPoint)m_Socket.LocalEndPoint;
            }
        }

        /// <summary>
        /// The RemoteEndPoint of this WebSocektClient
        /// </summary>
        public IPEndPoint RemoteEndPoint
        {
            get
            {
                return (IPEndPoint)remoteEndPoint;
            }
        }

        /// <summary>
        /// The Listener thread in which sending and recieving happens on
        /// </summary>
        protected Thread Listener
        {
            get
            {
                return m_Worker;
            }
        }

        #endregion

    }
}